#pragma once

#include <vector>

#ifdef PUNCH_MSGPACK
#include <msgpack.hpp>
#endif

namespace mdtx::punch::tradesim
{
    /**
     * @brief Records instrument properties
     *
     * @tparam QT quote type
     */
    template <typename QT>
    class Instrument
    {
    public:
        using id_type = typename QT::id_type;
        using quant_type = typename QT::quant_type;

        struct InstrumentT
        {
            quant_type lot_min, lot_size;
            double short_rate, long_rate;
            bool T0, margin_short, margin_long;

            quant_type adjust_quant(quant_type quant) const
            {
                if (quant < lot_min)
                {
                    return quant_type{};
                }
                else
                {
                    size_t nlot = (quant - lot_min) / lot_size;
                    return lot_min + nlot * lot_size;
                }
            }

#ifdef PUNCH_MSGPACK
            MSGPACK_DEFINE_ARRAY(lot_min, lot_size, short_rate, long_rate, T0, margin_short, margin_long);
#endif
        };

        static auto create(quant_type lot_min, quant_type lot_size, double short_rate, double long_rate, bool T0)
        {
            return InstrumentT{lot_min, lot_size, short_rate, long_rate, T0, short_rate > 0, long_rate > 0};
        }

    private:
        size_t n;
        std::vector<InstrumentT> val;

    public:
        Instrument(size_t n, quant_type lot_min, quant_type lot_size, double short_rate, double long_rate, bool T0)
            : n(n),
              val(n, create(lot_min, lot_size, short_rate, long_rate, T0))
        {
        }

        size_t num_instr() const
        {
            return n;
        }

        auto &get(id_type ins_id)
        {
            return val[ins_id];
        }

        const auto &get(id_type ins_id) const
        {
            return val[ins_id];
        }

        void set(id_type ins_id, quant_type lot_min, quant_type lot_size, double short_rate, double long_rate, bool T0)
        {
            auto &ins = get(ins_id);
            ins.lot_min = lot_min;
            ins.lot_size = lot_size;
            ins.short_rate = short_rate;
            ins.long_rate = long_rate;
            ins.T0 = T0;
            ins.short_rate = short_rate > 0;
            ins.long_rate = long_rate > 0;
        }

        void set_lot(id_type ins_id, quant_type lot_min, quant_type lot_size)
        {
            auto &ins = get(ins_id);
            ins.lot_min = lot_min;
            ins.lot_size = lot_size;
        }

        void set_rate(id_type ins_id, double short_rate, double long_rate)
        {
            auto &ins = get(ins_id);
            ins.short_rate = short_rate;
            ins.long_rate = long_rate;
            ins.short_rate = short_rate > 0;
            ins.long_rate = long_rate > 0;
        }

        void set_T0(id_type ins_id, bool T0)
        {
            auto &ins = get(ins_id);
            ins.T0 = T0;
        }
    };
}
